package ;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;

import Movable;
import SceneCoord2d;

/**
 * ...
 * @author m
 */

class SceneView extends Scene
{	
	var mXoff: Int;
	var mYoff: Int;
		
	var mTileWidth: Int;
	var mTileHeight: Int;
	
	var mBackgroundData: BitmapData;
	var mBackground: Bitmap;
	
	var mLastStep: Float;
	var mStepsPerSecond: Float;
		
	var mUpdateSpeed: Int;
	var mUpdateCount: Int;
	
	var mGuiObjects: Array<Movable>;
	
	 
	public function new() 
	{
		super();
		
		
		// prepare field
		mXoff = 0;
		mYoff = 0;
		mTileWidth = 40;
		mTileHeight = 40;
			
		
		// prepare counters
		mStepsPerSecond = 100;
		mLastStep = 0;
		mUpdateSpeed = 4;	// every 4 frame is logic update
		
		
		// prepare objects
		mGuiObjects = new Array<Movable>();
		
		
		// prepare backgrounds
		mBackgroundData = new BitmapData(640, 480);
		mBackground = new Bitmap(mBackgroundData);
		addChild(mBackground);
		
		
		// load resources
		Resources.Init();
		
		LoadResources();
		
		Resources.Inst.addEventListener(Resources.IMGS_LOADED, Start);

		Resources.Inst.Load();
	}
	
	
	public function SetBackground(id: Int)
	{			
		mBackgroundData.copyPixels(Resources.Inst.GetBitmapData(id), mBackgroundData.rect, new Point());
	}
	
	
	public function SetTileSize(w: Int, h: Int)
	{
		mTileWidth = w;
		mTileHeight = h;
	}
	
	
	public function SetFieldOffset(x: Int, y: Int)
	{
		mXoff = x;
		mYoff = y;	
	}
	
	
	public function GetViewPos(pos: SceneCoord2d): Point
	{
		var res: Point = new Point();
		res.x = mXoff + pos.col * mTileWidth;
		res.y = mYoff + pos.row * mTileHeight;
		return res;
	}	

	
	public function GetFieldPos(viewX: Float, viewY: Float): SceneCoord2d
	{
		var res: SceneCoord2d = new SceneCoord2d();
		res.col = Math.floor( (viewX - mXoff) / mTileWidth );
		res.row = Math.floor( (viewY - mYoff) /  mTileHeight );
		return res;
	}	
	
	
	override function Reset()
	{
		super.Reset();
		
		mGuiObjects = [];
	}
	
	
	override function Start(void)
	{
		super.Start(void);
		
		//trace("SceneView::Start");
				
		stage.addEventListener(Event.ENTER_FRAME, OnEnter);
		
		addEventListener(MouseEvent.MOUSE_DOWN, OnPress);
		addEventListener(MouseEvent.MOUSE_UP, OnRelease);		
		
		mLastStep = haxe.Timer.stamp();
		mUpdateCount = mUpdateSpeed;
	}
	
	
	override function StartNewLevel()
	{
		super.StartNewLevel();
		
		CreateHud();
	}
	
	
	private function CreateHud();
	
	
	public function LoadResources()
	{
		// does nothing by default
	}
	
	
	public function AddSprite(id: Int, resourceName: String)
	{
		Resources.Inst.AddSprite(id, resourceName);
	}
	
	
	public function AddGuiObject(obj: Movable)
	{
		mGuiObjects.push(obj);
	}

	
	public function RemoveGuiObject(obj: Movable)
	{
		mGuiObjects.remove(obj);
		
		if (obj.parent != null) {
			obj.parent.removeChild(obj);
		}
		
		obj = null;
	}
	
	
	function OnEnter(e:flash.events.Event)
    {
		var now = haxe.Timer.stamp();
		// Do a number of descrete steps based on the mStepsPerSecond.
		var steps = Math.floor( (now-mLastStep) * mStepsPerSecond );
		// Since the mStepsPerSecond may change in the Update call, make sure
		//  we do all our calculations before we call Update.
		mLastStep += steps / mStepsPerSecond;
		var fractional_step = (now-mLastStep) * mStepsPerSecond;

		for (i in 0...steps) {
		    Update();
		}

		// This helps flash efficiently update the bitmap, batching the changes
		mBackgroundData.lock();

		// fractional_step is something we don't use, but it could be used to do some
		//  dead-reckoning in the render code to smooth out the display.
		Render(fractional_step);

		// This completes the batching
		mBackgroundData.unlock();
    }
	
	
	function Update()
	{
		mUpdateCount--;
		
		var updatePercent: Float = (mUpdateSpeed - mUpdateCount) / mUpdateSpeed;
		
		// update logic every N frames
		if (mUpdateCount == 0) {
			mUpdateCount = mUpdateSpeed;
			
			// check user input
			if (mClickAction == true)
				OnFieldClick(mClickPos);

			if (mReleaseAction == true)
				OnFieldRelease(mReleasePos);

			// advance logic
			UpdateLogic();
			
			// invalidate actions
			mClickAction = mReleaseAction = false;
		}

		//trace(updatePercent);
	
		// update items
		for (obj in mGuiObjects) {
			if (obj.Update(mUpdateSpeed - mUpdateCount, mUpdateSpeed) == false) {
				// kill this object
				RemoveGuiObject(obj);
			}
		}	
	}
	
	
	function Render(inExtra:Float)
	{
		for (obj in mGuiObjects) {
			obj.Draw();
		}	
	}
	
	
	function OnPress(e:MouseEvent): Void
	{
		mClickPos = GetFieldPos(e.stageX, e.stageY);
		mClickAction = true;
		
		//trace("Press: " + mClickPos.row + ":" + mClickPos.col);
		//trace(e);
	}

	
	function OnRelease(e:MouseEvent): Void
	{
		mReleasePos = GetFieldPos(e.stageX, e.stageY);
		mReleaseAction = true;
		
		//trace("Release");
	}
	
}